home *** CD-ROM | disk | FTP | other *** search
/ SPACE 1 / SPACE - Library 1 - Volume 1.iso / program / 168 / mk.c < prev    next >
C/C++ Source or Header  |  1988-03-31  |  17KB  |  819 lines

  1. /* 
  2.  * simple make program 
  3.  *
  4.  *    make [options] [targets]
  5.  *
  6.  *    options are
  7.  *        -i        ignore non-zero program returns
  8.  *        -f file        makefile name (default is "makefile")
  9.  *        -n        don't execute any commands
  10.  *        -t        just touch the targets, don't build 
  11.  *        -r        ignore built-in rules
  12.  *        -d        debug flag (noisy output)
  13.  *        -h        hold the screen
  14.  *
  15.  * this program reads a makefile to build the named targets
  16.  * a makefile contains rules, macro definitions, or dependencies
  17.  * an example makefile might be:
  18.  *
  19.  *     # this is a sample makefile for a spreadsheet
  20.  *     OBJ = main.s io.s calc.s
  21.  *     ss: $(OBJ) ss.h
  22.  *        d:as.ttp d:ttp.s $(OBJ) -L d:lib.a
  23.  *
  24.  * there are built-in rules to convert .c to .s, .c to .ttp, and .c to .prg
  25.  */
  26.  
  27. #include <stdio.h>
  28.  
  29. #define MAXLINE    160
  30. #define MAXSYM    100
  31. #define MAXRULE    30
  32. #define MAXARG    75
  33.  
  34. #define TIME    0x2C
  35. #define DATE    0x2A
  36. #define EXEC    0x4B
  37. #define RENAME    0x56
  38. #define GSDTOF    0x57
  39. #define SETDRV    0x0E
  40. #define GETDRV    0x19
  41. #define GETDSK    0x36
  42. #define MALLOC    0x48
  43.  
  44. typedef struct x0 {        /* structure for command lists        */
  45.     char *cmd;        /*    pointer to the command line    */
  46.     struct x0 *next;    /*    pointer to the next one        */
  47. } Command;
  48.  
  49. typedef struct x1 {        /* structure for dependencies        */
  50.     int    dep;        /*    name of a parent        */
  51.     struct x1 *next;    /*    pointer to next parent        */
  52. } Depend;
  53.  
  54. typedef struct {        /* structure for rules            */
  55.     int     from;        /*    the extension of the parent    */
  56.     int    to;        /*    the extension of the child    */
  57.     Command    *cmd;        /*     the commands to make the child    */
  58. } Rule;
  59.  
  60. FILE    *in;            /* makefile input file handle        */
  61. int    eof;            /* eof flag for makefile input stream    */
  62. int    no_exec = 0;        /* don't execute the commands        */
  63. int    debug = 0;        /* debug output flag            */
  64. int    ignore = 0;        /* ignore non-zero returns        */
  65. int    norules = 0;        /* no built-in rules            */
  66. int    hold = 1;        /* hold the screen (for GEM users    */
  67. char     line[MAXLINE];        /* buffer containing current input line    */
  68. char    extra[MAXLINE];        /* extra buffer for macro expansion    */
  69. char    name[MAXLINE];        /* space for making up names        */
  70. char     *macro[MAXSYM];        /* pointer to the macros        */
  71. char     *sym[MAXSYM];        /* pointer to the symbol names        */
  72. Depend    *depend[MAXSYM];    /* pointer to dependencies        */
  73. Command    *command[MAXSYM];    /* index into commands            */
  74. Rule    rules[MAXRULE];        /* rules                */
  75. int    lrule = 0;        /* last rule used            */
  76. int    firstsym = -1;        /* what's first dependency declared    */
  77. char    base[MAXLINE];        /* buffer for '*' macro            */
  78. char    target[MAXLINE];    /* buffer for '@' macro            */
  79.  
  80. char     *save();        /* save a string            */
  81. char    *alloc();        /* allocate space            */
  82. Command    *cmdlist();        /* make up a command list        */
  83. Command *add_cmd();        /* add a command to a command list    */
  84. long    date();            /* get the date of a file        */
  85.  
  86. char    *av[MAXARG];        /* argv for built-in's            */
  87. int    ac;            /* argc for built-in's            */
  88. FILE    *xin;            /* stdin for built-in commands        */
  89. FILE    *xout;            /* stdout for built-in commands        */
  90.  
  91. main(argc, argv) char *argv[]; {
  92.     int i, err, tch;
  93.     char *name;
  94.     i = 1;
  95.     name = "makefile";
  96.     tch = err = 0;
  97.     for (i = 1; i < argc && *argv[i] == '-'; i++) {
  98.         switch (argv[i][1]) {
  99.         case 'f': case 'F':
  100.             i++;
  101.             name = argv[i];
  102.             break;
  103.         case 'i': case 'I':
  104.             ignore = 1;
  105.             break;
  106.         case 'd': case 'D':
  107.             debug = 1;
  108.             break;
  109.         case 't': case 'T':
  110.             tch = 1;
  111.             break;
  112.         case 'n': case 'N':
  113.             no_exec = 1;
  114.             break;
  115.         case 'r': case 'R':
  116.             norules = 1;
  117.             break;
  118.         case 'h': case 'H':
  119.             hold = !hold;
  120.             break;
  121.         default:
  122.             printf("unknown option: %s\n", argv[i]);
  123.             err = 1;
  124.             break;
  125.         }
  126.     }
  127.     if (err) error("usage: make [-i -n -t -r -d -h -f file] [targets]\n");
  128.     if (tch) {
  129.         while (i < argc)
  130.             touch(argv[i++]);
  131.     }
  132.     else    {
  133.         init();
  134.         input(name);
  135.         if (i == argc && firstsym >= 0)
  136.             make(firstsym);
  137.         else    {
  138.             for ( ; i < argc; i++)
  139.                 if (!assign(argv[i]))
  140.                     make(lookup(argv[i]));
  141.         }
  142.     }
  143.     bye(0);
  144. }
  145.  
  146. /* initialize the symbol table and add built-in rules */
  147.  
  148. init() {
  149.     int i;
  150.     Command *cp;
  151.     rules[0].cmd = NULL;
  152.     for (i = 0; i < MAXSYM; i++)
  153.         sym[i] = NULL;
  154.     if (norules) return;
  155.     /* built-in rules */
  156.     cp = NULL;
  157.     cp = add_cmd(cp, " d:\\cc.ttp $*.c");
  158.     cp = add_cmd(cp, " d:\\as.ttp -o $*.ttp d:\\ttp.s yc.out -L d:\\lib.a");
  159.     cp = add_cmd(cp, " rm yc.out");
  160.     add_rule(".c", ".ttp", cp);
  161.     cp = NULL;
  162.     cp = add_cmd(cp, " d:\\cc.ttp $*.c");
  163.     cp = add_cmd(cp, 
  164.     " d:\\as.ttp -o $*.prg d:\\prg.s yc.out -L d:\\gem.a d:\\lib.a");
  165.     cp = add_cmd(cp, " rm yc.out");
  166.     add_rule(".c", ".prg", cp);
  167.     cp = NULL;
  168.     cp = add_cmd(cp, " d:\\cc.ttp -o $*.s $*.c");
  169.     add_rule(".c", ".s", cp);
  170. }
  171.  
  172. /* read in and parse the makefile */
  173.  
  174. input(name) char *name; {
  175.     if (!findfile(name))
  176.         return;
  177.     getline();
  178.     while (!eof) {
  179.         if (*line == '#')
  180.             getline();
  181.         else if (macdef())
  182.             getline();
  183.         else if (*line == '.')
  184.             rule();
  185.         else if (*line > ' ')
  186.             dependency();
  187.         else    getline();
  188.     }
  189.     fclose(in);
  190. }
  191.  
  192. /* search high and low for a makefile */
  193.  
  194. findfile(name) char *name; {
  195.     int i, drv, r;
  196.     /* someday, turn a full pathname into a chdir */
  197.     if (in = fopen(name, "r")) /* try current directory first */
  198.         return 1;
  199.     drv = trap(1, GETDRV);
  200.     for (i = 0; i < 4; i++) { /* try A:, B:, C:, D: */
  201.         r = trap(1, SETDRV, i);
  202.         if (in = fopen(name, "r")) {
  203.             printf("  run from %c:\n", i + 'A');
  204.             return 1;
  205.         }
  206.     }
  207.     trap(1, SETDRV, drv);
  208.     return 0;
  209. }
  210.  
  211. /* read in a line from the makefile */
  212.  
  213. getline() {
  214.     int c;
  215.     char *l;
  216.     l = line;
  217.     c = getch();
  218.     while (c != EOF && c != '\n') {
  219.         if (c == '\\') {
  220.             if ((c = getch()) == '\n')
  221.                 c = ' ';
  222.             else    *l++ = '\\';
  223.         }
  224.         *l++ = c;
  225.         if (c <= ' ') { /* skip spaces */
  226.             while (c != EOF && c != '\n' && c <= ' ')
  227.                 c = getch();
  228.         }
  229.         else    c = getch();
  230.     }
  231.     *l = 0;
  232.     eof = (c == EOF);
  233. }
  234.  
  235. /* get a character from the file, strip out '\r' */
  236.  
  237. getch() {
  238.     int c;
  239.     while ((c = getc(in)) == '\r')
  240.         ;
  241.     return c;
  242. }
  243.  
  244. /* expand all the macro's in the current line */
  245.  
  246. expand() { 
  247.     int done;
  248.     char *e, *l, *m;
  249.     while (1) {
  250.         e = extra;
  251.         l = line;
  252.         done = 1;
  253.         while (*e++ = *l++)
  254.             if (*l == '$') 
  255.                 done = 0;
  256.         if (done) 
  257.             break;
  258.         e = extra;
  259.         l = line;
  260.         while (*e) {
  261.             if (*e == '$') {
  262.                 e++;
  263.                 m = name;
  264.                 if (*e == '(') { /* multi letter macro */
  265.                     e++;
  266.                     while (*e != ')') 
  267.                         *m++ = *e++;
  268.                     e++;
  269.                 }
  270.                 else    *m++ = *e++; /* one letter macro */
  271.                 *m = 0;
  272.                 if (m = macro[lookup(name)]) {
  273.                     while (*m) 
  274.                         *l++ = *m++;
  275.                 }
  276.                 else    error("$(%s) not defined", name);
  277.             }
  278.             else    *l++ = *e++;
  279.         }
  280.         *l = 0;
  281.     }
  282. }
  283.  
  284. /* read in a rule, ".from.to:" followed by command lines */
  285.  
  286. rule() {
  287.     int i, j;
  288.     char to[10], from[10], *l;
  289.     l = line;
  290.     i = j = 0;
  291.     if (*l != '.') 
  292.         error("bad rule entry");
  293.     do { from[j++] = *l++; } while (*l && *l != '.');
  294.     from[j] = 0;
  295.     if (*l != '.') 
  296.         error("bad rule entry");
  297.     j = 0;
  298.     do { to[j++] = *l++; } while (*l && *l != ':');
  299.     to[j] = 0;
  300.     add_rule(from, to, cmdlist());
  301. }
  302.  
  303. /* build a list of commands, return pointer to them */
  304.  
  305. Command *
  306. cmdlist() {
  307.     Command *r;
  308.     r = NULL;
  309.     getline();
  310.     while (*line && *line <= ' ') {
  311.         r = add_cmd(r, save(line));
  312.         getline();
  313.     }
  314.     return r;
  315. }
  316.  
  317. /* check for a command line macro def and process if there */
  318.  
  319. assign(s) char *s; {
  320.     char *p;
  321.     for (p = s; *p && *p != '='; p++)
  322.         ;
  323.     if (*p == '=') {
  324.         *p++ = 0;
  325.         add_mac(s, save(p));
  326.         return 1;
  327.     }
  328.     else    return 0;
  329. }
  330.  
  331. /* check for a macro def and process if there */
  332.  
  333. macdef() { 
  334.     char *l, *b, *e;
  335.     l = line;
  336.     while (*l && *l <= ' ')    l++;
  337.     b = l;
  338.     while (*l && *l > ' ')     l++;
  339.     e = l;
  340.     while (*l && *l <= ' ') l++;
  341.     if (*l++ == '=') { /* got a macro */
  342.         *e = 0;
  343.         add_mac(b, save(l));
  344.         return 1;
  345.     }
  346.     else    return 0;
  347. }
  348.  
  349. /* parse a dependency, "child [children]*: [parents]*" */
  350.  
  351. dependency() {
  352.     int i, j, target[20], depend[20];
  353.     Command *cmd;
  354.     expand();
  355.     i = namelist(target, 0);
  356.     if (line[i] != ':')
  357.         error("bad dependency rule");
  358.     i = namelist(depend, i+1);
  359.     cmd = cmdlist();
  360.     for (i = 0; target[i]; i++) {
  361.         for (j = 0; depend[j]; j++)
  362.             add_dep(target[i], depend[j]);
  363.         set_cmd(target[i], cmd);
  364.     }
  365.     if (firstsym < 0) 
  366.         firstsym = target[0];
  367. }
  368.  
  369. /* gather up a list of names in the input line */
  370.  
  371. namelist(list, i) int *list; {
  372.     int t, j;
  373.     t = 0;
  374.     while (line[i] && line[i] != ':') {
  375.         while (line[i] && line[i] <= ' ') 
  376.             i++;
  377.         j = 0;
  378.         if (line[i]) {
  379.             while (line[i] && line[i] > ' ' && line[i] != ':')
  380.                 name[j++] = line[i++];
  381.             name[j] = 0;
  382.             list[t++] = lookup(name);
  383.         }
  384.     }
  385.     list[t] = 0;
  386.     return i;
  387. }
  388.  
  389. /* add a new command to the end of a command list */
  390.  
  391. Command *
  392. add_cmd(cp, cmd) Command *cp; char *cmd; {
  393.     Command *p, *r;
  394.     p = alloc((short)sizeof(Command));
  395.     p->cmd = cmd;
  396.     p->next = NULL;
  397.     if (cp == NULL)
  398.         r = p;
  399.     else     {
  400.         r = cp;
  401.         while (cp->next)
  402.             cp = cp->next;
  403.         cp->next = p;
  404.     }
  405.     return r;
  406. }
  407.  
  408. /* add a parent (target) to a child (dependency) */
  409.  
  410. add_dep(target, dep) {
  411.     Depend *p;
  412.     if (debug) printf("add_dep(%s,%s)\n", sym[target], sym[dep]);
  413.     p = alloc((short)sizeof(Depend));
  414.     p->dep = dep;
  415.     p->next = depend[target];
  416.     depend[target] = p;
  417. }
  418.  
  419. /* add a macro */
  420.  
  421. add_mac(name, str) char *name, *str; {
  422.     if (debug) printf("add_mac(%s,%s)\n", name, str);
  423.     macro[lookup(name)] = str;
  424. }
  425.  
  426. /* add a command list to a target */
  427.  
  428. set_cmd(target, cp) Command *cp; {
  429.     if (debug) printf("set_cmd(%s,%lx)\n", sym[target], cp);
  430.     command[target] = cp;
  431. }
  432.  
  433. /* add a rule */
  434.  
  435. add_rule(from, to, cp) char *from, *to; Command *cp; {
  436.     if (debug) printf("add_rule(%s,%s,%lx)\n", from, to, cp);
  437.     rules[lrule].from = lookup(from);
  438.     rules[lrule].to = lookup(to);
  439.     rules[lrule].cmd = cp;
  440.     if (++lrule >= MAXRULE)
  441.         error("too many rules");
  442.     rules[lrule].cmd = NULL;
  443. }
  444.  
  445. /* build a child by first building the parents */
  446.  
  447. make(child) {
  448.     int mkflag;
  449.     long chdate;
  450.     Command *cp;
  451.     Depend *dp;
  452.  
  453.     if (debug) printf("make(%s)\n", sym[child]);
  454.     chdate = date(sym[child]);
  455.     mkflag = 0;
  456.  
  457.     if (dp = depend[child]) {
  458.         while (dp) {
  459.             make(dp->dep);
  460.             if (check(dp->dep, chdate))
  461.                 mkflag = 1;
  462.             dp = dp->next;
  463.         }
  464.     }
  465.     else    mkflag = 1;
  466.  
  467.     if (mkflag) {
  468.         strcpy(target, sym[child]);
  469.         add_mac("@", target);
  470.         if (cp = command[child]) {
  471.             unlink(target);
  472.             execute(cp);
  473.         }
  474.         else    chkrule(child, chdate);
  475.     }
  476. }
  477.  
  478. /* set the date/time of the named file to the present */
  479.  
  480. touch(name) char *name; {
  481.     int fd, r;
  482.     long dt;
  483.     if ((fd = open(name, 2)) >= 0) {
  484.         dt = (trap(1, DATE) && 0xFFFF) | (trap(1, TIME) << 16);
  485.         if (r = trap(1, GSDTOF, &dt, fd, 1))
  486.             perror(r, name);
  487.         close(fd);
  488.     }
  489.     else    error("cannot open %s (%d)\n", name, fd);
  490. }
  491.  
  492. /* get the date/time of the named file */
  493.  
  494. long
  495. date(name) char *name; {
  496.     int fd;
  497.     unsigned long dt;
  498.     if ((fd = open(name, 0)) >= 0) {
  499.         trap(1, GSDTOF, &dt, fd, 0);
  500.         dt = ((dt >> 16) & 0xFFFFL) | (dt << 16); /* swap words */
  501.         close(fd);
  502.     }
  503.     else    dt = 0L;
  504.     if (debug) printf("date of %s is %lx\n", name, dt);
  505.     return dt;
  506. }
  507.  
  508. /* compare the date/time of the named file against the child date */
  509.  
  510. check(parent, chdt) long chdt; {
  511.     long pardt;
  512.     pardt = date(sym[parent]);
  513.     return (pardt > chdt);
  514. }
  515.  
  516. /* see if there are any rules that we can use to build the child */
  517.  
  518. chkrule(child, chdate) long chdate; {
  519.     Rule *r;
  520.     char *s, *e;
  521.     int i, j, ext, parent, parbase;
  522.     s = sym[child];
  523.     for (j = i = 0; base[i] = s[i]; i++)
  524.         if (s[i] == '.') 
  525.             j = i;
  526.     if (j == 0) 
  527.         return 0;
  528.     e = &s[j];
  529.     base[j] = 0;
  530.     parbase = lookup(base);
  531.     ext = lookup(e);
  532.     for (i = lrule; --i >= 0; ) {
  533.         r = &rules[i];
  534.         if (r->to == ext) {
  535.             strcpy(extra, base);
  536.             strcat(extra, sym[r->from]);
  537.             parent = lookup(extra);
  538.             make(parent);
  539.             add_mac("*", sym[parbase]); 
  540.             if (check(parent, chdate)) {
  541.                 unlink(sym[child]);
  542.                 execute(r->cmd);
  543.                 return;
  544.             }
  545.         }
  546.     }
  547. }
  548.  
  549. /* execute a list of commands */
  550.  
  551. execute(cp) Command *cp; {
  552.     int r;
  553.     while (cp) {
  554.         strcpy(line, cp->cmd);
  555.         expand();
  556.         printf("  %s\n", line);
  557.         if (!no_exec && (r = system(line)) && !ignore)
  558.             perror(r, "command failed");
  559.         cp = cp->next;
  560.     }
  561. }
  562.  
  563. /* do something, either built-in (rm, mv) or exec a program */
  564.  
  565. args(s) char *s; { /* built and argc, argv for the built-in's */
  566.     char *strtok();
  567.     ac = 1;
  568.     s = strtok(s, " \t");
  569.     xin = stdin;
  570.     xout = stdout;
  571.     while (ac < MAXARG && s != NULL) {
  572.         if (*s == '>') {
  573.             if (s[1] == '>')
  574.                 xout = fopen(s+2, "a");
  575.             else    xout = fopen(s+1, "w");
  576.         }
  577.         else if (*s == '<')
  578.             xin = fopen(s+1, "r");
  579.         else    av[ac++] = s;
  580.         s = strtok(NULL, " \t");
  581.     }
  582.     av[ac] = NULL;
  583. }
  584.  
  585. rm() { /* remove a file */
  586.     int i, r, rr;
  587.     rr = 0;
  588.     for (i = 1; i < ac; i++) {
  589.         if ((r = unlink(av[i])) != 0 && r != -33) {
  590.             /* not deleted but was found */
  591.             perror(r, av[i]);
  592.             rr = r;
  593.         }
  594.     }
  595.     return rr;
  596. }
  597.  
  598. mv() { /* rename a file */
  599.     int r;
  600.     if (ac == 3) {
  601.         unlink(av[2]);
  602.         if (r = trap(1, RENAME, 0, av[1], av[2])) {
  603.             printf("rename didn't work (%d), try copy\n", r);
  604.             if ((r = cp()) == 0)
  605.                 unlink(av[1]);
  606.         }
  607.     }
  608.     return r;
  609. }
  610.  
  611. cp() { /* copy a file */
  612.     int r;
  613.     FILE *in, *out;
  614.     r = 1;
  615.     if (ac == 3) {
  616.         if (in = fopen(av[1], "rb")) {
  617.             if (out = fopen(av[2], "wb")) {
  618.                 xcat(in, out);
  619.                 r = 0;
  620.                 fclose(in);
  621.                 fclose(out);
  622.             }
  623.             else    error("cannot create %s\n", av[2]);
  624.             fclose(in);
  625.         }
  626.         else    error("cannot copy %s\n", av[1]);
  627.     }
  628.     return r;
  629. }
  630.  
  631. cat() { /* cat a file */
  632.     int i, r;
  633.     FILE *in;
  634.     r = 0;
  635.     for (i = 1; i < ac; i++) {
  636.         if ((in = fopen(av[i], "r")) == NULL) {
  637.             r = 1;
  638.             error("cannot cat %s", av[i]);
  639.         }
  640.         else    xcat(in, xout);
  641.     }
  642.     return r;
  643. }
  644.  
  645. xcat(i, o) FILE *i, *o; { /* do the real work of cat */
  646.     int c;
  647.     while ((c = getc(i)) != EOF)
  648.         putc(c, o);
  649.     fclose(i);
  650. }
  651.  
  652. grep() {
  653.     int i;
  654.     char *p, *fgets();
  655.     FILE *in, *fopen();
  656.     strlower(p = av[1]);
  657.     for (i = 2; i < ac; i++) {
  658.         if (in = fopen(av[i], "r")) {
  659.             while (fgets(extra, MAXLINE, in)) {
  660.                 strlower(extra);
  661.                 if (match(p, extra))
  662.                     printf("%s:\t%s", av[i], extra);
  663.             }
  664.             fclose(in);
  665.         }
  666.         else    printf("cannot open %s\n", av[i]);
  667.     }
  668.     return 0;
  669. }
  670.  
  671. match(pat, line) char *pat, *line; {
  672.     char *p, *l;
  673.     while (*line) {
  674.         p = pat;
  675.         l = line++;
  676.         while (*p++ == *l++)
  677.             if (*p == 0)
  678.                 return 1;
  679.     }
  680.     return 0;
  681. }
  682.  
  683. xfree() {
  684.     long m, dsz[4];
  685.     int c, drv;
  686.     if (ac > 1 && (c = *av[1]) >= 'A' && c <= 'P')
  687.         drv = c - 'A';
  688.     else    drv = trap(1, GETDRV);
  689.     trap(1, GETDSK, dsz, drv + 1);
  690.     m = trap(1, MALLOC, -1L);
  691.     printf("%ld free bytes in memory, ", m);
  692.     printf("%ld free bytes on %c:\n", dsz[0]*dsz[2]*dsz[3], drv+'A');
  693.     return 0;
  694. }
  695.  
  696. struct bltin {
  697.     char *name;
  698.     int (*func)();
  699. } btbl[] = {
  700.     { "rm", rm },
  701.     { "mv", mv },
  702.     { "cp", cp },
  703.     { "cat", cat },
  704.     { "grep", grep },
  705.     { "free", xfree },
  706.     { NULL, NULL }
  707. };
  708.  
  709. system(s) char *s; {
  710.     int i, r;
  711.     char *p;
  712.     while (*s && *s <= ' ') 
  713.         s++;
  714.     for (p = extra; *s && *s > ' '; )
  715.         *p++ = *s++;
  716.     *p = 0;
  717.     for (i = 0; btbl[i].name != NULL; i++) {
  718.         if (strcmp(extra, btbl[i].name) == 0) {
  719.             args(s);
  720.             r = (*btbl[i].func)();
  721.             if (xin != stdin) 
  722.                 fclose(xin);
  723.             if (xout != stdout) 
  724.                 fclose(xout);
  725.             return r;
  726.         }
  727.     }
  728.     if ((r = strlen(s+1)) >= 128)
  729.         error("command line too long");
  730.     else    {
  731.         *s = r;
  732.         r = trap(1, EXEC, 0, extra, s, "");
  733.     }
  734.     return r;
  735. }
  736.  
  737. /* symbol table lookup */
  738.  
  739. lookup(s) char *s; {
  740.     int i, start;
  741.     upper(s); /* too bad TOS filenames are always uppercase */
  742.     start = i = *s;
  743.     while (sym[i]) {
  744.         if (strcmp(s, sym[i]) == 0)
  745.             return i;
  746.         if (++i >= MAXSYM) 
  747.             i = 0;
  748.         if (i == start)
  749.             error("too many symbols");
  750.     }
  751.     sym[i] = save(s);
  752.     command[i] = macro[i] = depend[i] = 0L;
  753.     return i;
  754. }
  755.  
  756. /* convert a string to all uppercase in place */
  757.  
  758. upper(s) char *s; {
  759.     register int c;
  760.     for ( ; c = *s; s++) {
  761.         if (c >= 'a' && c <= 'z')
  762.             *s = c - 'a' + 'A';
  763.     }
  764. }
  765.  
  766. /* save a string */
  767.  
  768. char *
  769. save(s) char *s; {
  770.     char *r, *alloc();
  771.     r = alloc(strlen(s)+1);
  772.     strcpy(r, s);
  773.     return r;
  774. }
  775.  
  776. /* allocate some space */
  777.  
  778. char *
  779. alloc(n) {
  780.     char *r, *malloc();
  781.     if ((r = malloc(n)) == NULL)
  782.         error("out of free space");
  783.     return r;
  784. }
  785.  
  786. bye(n) {
  787.     if (hold) {
  788.         printf("(press any char)\n");
  789.         getchar();
  790.     }
  791.     exit(n);
  792. }
  793.  
  794. perror(r, s) char *s; {
  795.     char *p;
  796.     switch (r) {
  797.     case -32: p = "invalid function number"; break;
  798.     case -33: p = "file not found"; break;
  799.     case -34: p = "pathname not found"; break;
  800.     case -35: p = "too many open files"; break;
  801.     case -36: p = "access not possible"; break;
  802.     case -37: p = "invalid handle"; break;
  803.     case -39: p = "not enough memory"; break;
  804.     case -46: p = "invalid drive spec"; break;
  805.     case -49: p = "no more files"; break;
  806.     default: sprintf(p = extra, "error %d", r);
  807.     }
  808.     error("%s: %s", p, s);
  809. }
  810.  
  811. /* complain and get out */
  812.  
  813. error(s, a, b, c, d) char *s; long a, b, c, d; {
  814.     printf("** ");
  815.     printf(s, a, b, c, d);
  816.     printf("\n");
  817.     bye(1);
  818. }
  819.